package com.cash.app.net

import com.cash.app.constant.AppConstant.API_RETRY_COUNT
import com.cash.app.constant.AppConstant.PATH_API_RETRY
import com.cash.app.utils.TLog
import com.cash.app.utils.UrlContentUtils
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import java.io.IOException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException

class RetryChangeDomainInterceptor : Interceptor {

    private val TAG = "RetryChangeDomainInterceptor"

    private val retryExceptions = setOf(
        UnknownHostException::class.java,
        SocketTimeoutException::class.java,
        ConnectException::class.java
    )

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest: Request = chain.request()
        val originalUrl: String = originalRequest.url.toString()
        TLog.d("originalUrl=$originalUrl", TAG)

        var shouldRetry = false
        var response: Response? = null

        try {
            response = chain.proceed(originalRequest)
            // If original request succeeds, return directly
            if (response.isSuccessful) {
                TLog.d("success originalUrl=$originalUrl ", TAG)
                return response
            }
            TLog.d("originalUrl=${response.code} ", TAG)
        } catch (e: IOException) {
            if (shouldRetryWithBackupHost(e)) {
                TLog.d("shouldRetryWithBackupHost", TAG, e)
                shouldRetry = true
            } else {
                TLog.d("should not RetryWithBackupHost", TAG)
                return chain.proceed(originalRequest)
            }
        }

        // If we don't need to retry, return the original response
        if (!shouldRetry && response != null) {
            return response
        }

        // Check if we need to retry with backup URL
        val backupUrl = UrlContentUtils.readUrlContent(PATH_API_RETRY)
        if (backupUrl.isNotEmpty()) {
            // Parse backup URL
            val backupUrlObj = backupUrl.toHttpUrl()
            val backupUrlHost = backupUrlObj.host
            val backupUrlPort = backupUrlObj.port
            TLog.d("backupUrlHost=$backupUrlHost", TAG)
            TLog.d("backupUrlPort=$backupUrlPort", TAG)

            // Try with backup URL
            var retryCount = 0
            while (retryCount < API_RETRY_COUNT) {
                // Close previous response before making new request
                response?.close()

                try {
                    val newUrl =
                        originalRequest.url.newBuilder().host(backupUrlHost).port(backupUrlPort)
                            .build()
                    TLog.d("$retryCount->newUrl=$newUrl", TAG)

                    val newRequest = originalRequest.newBuilder().url(newUrl).build()

                    response = chain.proceed(newRequest)

                    if (response.isSuccessful) {
                        TLog.d("$retryCount->backupUrl=$newUrl success!", TAG)
                        RetrofitClient.updateBaseUrl(backupUrl)
                        return response
                    }

                    TLog.d("$retryCount->backupUrl=$newUrl fail!", TAG)
                } catch (e: IOException) {
                    TLog.d("$retryCount->backupUrl=$backupUrl exception!", TAG, e)
                }
                retryCount++
            }
        }

        // If all retries failed, return the last response or try original request again
        return response ?: chain.proceed(originalRequest)
    }

    private fun shouldRetryWithBackupHost(e: IOException): Boolean {
        for (clazz in retryExceptions) {
            if (clazz.isInstance(e)) {
                return true
            }
        }
        return false
    }
}